<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8"/>
    <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
    <title>黎擎的便签墙</title>

    <!-- 引入请求 API -->
    <script src="assets/api/messagesApi.js"></script>
    <script src="assets/api/colorsApi.js"></script>

    <!-- 引入默认便签信息和颜色 -->
    <script src="assets/js/messages.js"></script>
    <script src="assets/js/colors.js"></script>

    <!-- 引入全局样式 -->
    <link href="./assets/css/index.css" rel="stylesheet"/>
</head>
<body>

<div id="board"></div>

<script>

    // 是否使用服务器数据，默认使用本地数据
    // server | local
    // let isServer = "server"
    let isServer = "local"

    const board = document.getElementById('board')

    const cardStates = new WeakMap()
    // Reserve a very high层级给全屏卡片，避免被后续元素覆盖
    const MAXIMIZED_LAYER = 1000000
    let activeMaximizedCard = null
    let isMobile =
        window.matchMedia('(pointer: coarse)').matches ||
        window.innerWidth <= 768
    const maxCards = isMobile ? 120 : 180 // 限制 DOM 节点数量，减轻移动端压力
    const initialCardCount = isMobile ? 18 : 30
    const spawnInterval = isMobile ? 800 : 500
    let zIndexCursor = 200

    document.body.classList.toggle('is-mobile', isMobile)

    function randomFrom(array) {
        return array[Math.floor(Math.random() * array.length)]
    }

    function clamp(value, min, max) {
        return Math.min(Math.max(value, min), max)
    }

    function applyTransform(card, state) {
        const scale = state.scale ?? 1
        const translateX = state.translateX ?? 0
        const translateY = state.translateY ?? 0
        const angle = state.angle ?? 0
        card.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale}) rotate(${angle}deg)`
    }

    function bringToFront(card) {
        if (card === activeMaximizedCard) {
            card.style.zIndex = MAXIMIZED_LAYER
            return
        }

        zIndexCursor += 1
        if (activeMaximizedCard && zIndexCursor >= MAXIMIZED_LAYER) {
            zIndexCursor = MAXIMIZED_LAYER - 1
        }

        card.style.zIndex = zIndexCursor
    }

    function setupCardInteractions(card) {
        const header = card.querySelector('.card-header')
        const closeBtn = card.querySelector('.control.close')
        const minimizeBtn = card.querySelector('.control.minimize')
        const maximizeBtn = card.querySelector('.control.maximize')

        closeBtn.addEventListener('click', event => {
            event.stopPropagation()
            closeCard(card)
        })

        minimizeBtn.addEventListener('click', event => {
            event.stopPropagation()
            minimizeCard(card)
        })

        maximizeBtn.addEventListener('click', event => {
            event.stopPropagation()
            toggleMaximize(card)
        })

        header.addEventListener('pointerdown', event => {
            if (event.pointerType === 'touch') return
            startDrag(event, card)
        })

        card.addEventListener('pointerdown', () => {
            bringToFront(card)
        })

        header.addEventListener('dblclick', event => {
            if (!event.target.closest('.control')) {
                toggleMaximize(card)
            }
        })
    }

    function closeCard(card) {
        const state = cardStates.get(card)
        if (!state || state.closing) return
        if (card === activeMaximizedCard) {
            activeMaximizedCard = null
        }
        state.closing = true
        state.scale = 0.1
        card.style.opacity = '0'
        applyTransform(card, state)

        const handleTransitionEnd = event => {
            if (event.propertyName === 'opacity') {
                card.removeEventListener('transitionend', handleTransitionEnd)
                card.remove()
            }
        }

        card.addEventListener('transitionend', handleTransitionEnd)
    }

    function minimizeCard(card) {
        const state = cardStates.get(card)
        if (!state || state.closing) return

        // 最小化动画：缩小并淡出到底部，结束时移除节点释放内存
        const runMinimize = () => {
            state.closing = true
            bringToFront(card)
            const bottom = Math.max(window.innerHeight - 24, 0)
            const targetLeft = clamp(
                state.left,
                16,
                Math.max(window.innerWidth - card.offsetWidth - 16, 16)
            )

            state.left = targetLeft
            state.top = bottom
            state.scale = 0.1
            state.angle = 0
            card.style.left = `${targetLeft}px`
            card.style.top = `${bottom}px`
            card.style.opacity = '0.35'
            applyTransform(card, state)

            const handleTransitionEnd = event => {
                if (event.propertyName === 'transform') {
                    card.removeEventListener('transitionend', handleTransitionEnd)
                    card.remove()
                }
            }

            card.addEventListener('transitionend', handleTransitionEnd)
        }

        if (state.maximized) {
            activeMaximizedCard = null
            state.maximized = false
            card.classList.remove('maximized')
            card.style.borderRadius = '12px'
            state.left = 0
            state.top = 0
            state.scale = 1
            state.angle = 0
            applyTransform(card, state)

            requestAnimationFrame(() => {
                requestAnimationFrame(runMinimize)
            })
            return
        }

        runMinimize()
    }

    function toggleMaximize(card) {
        const state = cardStates.get(card)
        if (!state || state.closing) return

        if (state.maximized) {
            restoreFromMaximize(card, state)
        } else {
            maximizeCard(card, state)
        }
    }

    function maximizeCard(card, state) {
        state.beforeMaximize = {
            left: state.left,
            top: state.top,
            scale: state.scale ?? 1,
            width: card.offsetWidth,
            height: card.offsetHeight,
            angle: state.angle ?? 0
        }

        card.classList.add('maximized')
        card.style.left = '0px'
        card.style.top = '0px'
        card.style.width = `${window.innerWidth}px`
        card.style.height = `${window.innerHeight}px`
        card.style.borderRadius = '0'

        state.left = 0
        state.top = 0
        state.scale = 1
        state.angle = 0
        applyTransform(card, state)
        activeMaximizedCard = card
        bringToFront(card)
        state.maximized = true
    }

    function restoreFromMaximize(card, state) {
        const previous = state.beforeMaximize
        if (!previous) return

        card.classList.remove('maximized')
        card.style.left = `${previous.left}px`
        card.style.top = `${previous.top}px`
        card.style.width = `${previous.width}px`
        card.style.height = `${previous.height}px`
        card.style.borderRadius = '12px'

        state.left = previous.left
        state.top = previous.top
        state.scale = previous.scale ?? 1
        state.angle = previous.angle ?? state.angle ?? 0
        applyTransform(card, state)
        state.maximized = false
        if (activeMaximizedCard === card) {
            activeMaximizedCard = null
        }
        bringToFront(card)
        state.lastPosition = {left: state.left, top: state.top}

        setTimeout(() => {
            if (!state.maximized) {
                card.style.width = ''
                card.style.height = ''
                state.width = card.offsetWidth
                state.height = card.offsetHeight
            }
        }, 360)
    }

    function startDrag(event, card) {
        const control = event.target.closest('.control')
        if (control) return

        const state = cardStates.get(card)
        if (!state || state.closing || state.maximized) return

        // 鼠标拖拽使用 rAF 节流，避免频繁触发布局计算
        event.preventDefault()
        bringToFront(card)

        const header = card.querySelector('.card-header')
        card.classList.add('dragging')
        header.classList.add('dragging')

        state.dragging = true
        state.dragOffsetX = event.clientX - state.left
        state.dragOffsetY = event.clientY - state.top

        let dragFrame = null
        let pendingLeft = state.left
        let pendingTop = state.top

        const commitDrag = () => {
            dragFrame = null
            const maxLeft = Math.max(window.innerWidth - card.offsetWidth, 0)
            const maxTop = Math.max(window.innerHeight - card.offsetHeight, 0)
            state.left = clamp(pendingLeft, -card.offsetWidth * 0.4, maxLeft)
            state.top = clamp(pendingTop, -card.offsetHeight * 0.4, maxTop)
            card.style.left = `${state.left}px`
            card.style.top = `${state.top}px`
        }

        const handlePointerMove = moveEvent => {
            if (!state.dragging) return

            pendingLeft = moveEvent.clientX - state.dragOffsetX
            pendingTop = moveEvent.clientY - state.dragOffsetY
            if (dragFrame === null) {
                dragFrame = requestAnimationFrame(commitDrag)
            }
        }

        const handlePointerUp = () => {
            state.dragging = false
            state.lastPosition = {left: state.left, top: state.top}
            card.classList.remove('dragging')
            header.classList.remove('dragging')
            if (dragFrame !== null) {
                cancelAnimationFrame(dragFrame)
                commitDrag()
            }
            document.removeEventListener('pointermove', handlePointerMove)
            document.removeEventListener('pointerup', handlePointerUp)
        }

        document.addEventListener('pointermove', handlePointerMove)
        document.addEventListener('pointerup', handlePointerUp)
    }

    function createCard() {

        const card = document.createElement('div')
        card.className = 'card'

        const color = randomFrom(defaultColors)
        const angleRange = isMobile ? 6 : 10
        const angle = (Math.random() - 0.5) * angleRange
        const cardWidth = isMobile ? 180 : 220
        const cardHeight = isMobile ? 130 : 140
        const horizontalMargin = isMobile ? 12 : 16
        const verticalMargin = isMobile ? 12 : 20
        const left =
            horizontalMargin +
            Math.random() *
            Math.max(window.innerWidth - cardWidth - horizontalMargin * 2, 0)
        const top =
            verticalMargin +
            Math.random() *
            Math.max(window.innerHeight - cardHeight - verticalMargin * 2, 0)

        card.style.background = color
        card.style.left = `${left}px`
        card.style.top = `${top}px`
        if (activeMaximizedCard && zIndexCursor >= MAXIMIZED_LAYER - 2) {
            zIndexCursor = MAXIMIZED_LAYER - 2
        }
        card.style.zIndex = ++zIndexCursor

        card.innerHTML = `
					<div class="card-header">
						<div class="window-controls">
							<button class="control close" type="button" aria-label="关闭"></button>
							<button class="control minimize" type="button" aria-label="最小化"></button>
							<button class="control maximize" type="button" aria-label="最大化"></button>
						</div>
						<div class="card-title">温馨提示</div>
					</div>
					<div class="card-body">${randomFrom(defaultMessages)}</div>
				`

        const state = {
            angle,
            scale: isMobile ? 0.85 : 0.7,
            translateX: 0,
            translateY: 0,
            left,
            top,
            maximized: false,
            closing: false,
            lastPosition: {left, top}
        }

        cardStates.set(card, state)
        applyTransform(card, state)
        board.appendChild(card)

        state.width = card.offsetWidth
        state.height = card.offsetHeight

        requestAnimationFrame(() => {
            state.scale = 1
            applyTransform(card, state)
            card.style.opacity = '1'
        })

        setupCardInteractions(card)

        if (board.children.length > maxCards) {
            const oldest = board.firstElementChild
            if (oldest && oldest !== card) {
                oldest.remove()
            }
        }
    }

    for (let i = 0; i < initialCardCount; i++) {
        setTimeout(createCard, i * (isMobile ? 500 : 300))
    }

    setInterval(() => {
        createCard()
    }, spawnInterval)

    window.addEventListener('resize', () => {
        isMobile =
            window.matchMedia('(pointer: coarse)').matches ||
            window.innerWidth <= 768
        // 视口变化时重新切换移动端模式，并同步全屏卡片的宽高
        document.body.classList.toggle('is-mobile', isMobile)

        document.querySelectorAll('.card.maximized').forEach(card => {
            card.style.width = `${window.innerWidth}px`
            card.style.height = `${window.innerHeight}px`
        })
    })

    // -------------------------------------

    // 自定义右键菜单
    function setupCustomContextMenu() {
        const contextMenu = document.createElement('div');
        contextMenu.id = 'custom-context-menu';
        contextMenu.className = 'custom-menu';
        contextMenu.innerHTML = `
        <div class="menu-item" id="edit-messages">便签消息管理</div>
        <div class="menu-divider"></div>
        <div class="menu-item" id="edit-colors">便签颜色管理</div>
    `;
        document.body.appendChild(contextMenu);

        // 阻止默认右键菜单
        document.addEventListener('contextmenu', function(e) {
            e.preventDefault();

            const menu = document.getElementById('custom-context-menu');
            menu.style.display = 'block';
            menu.style.left = e.pageX + 'px';
            menu.style.top = e.pageY + 'px';

            // 为菜单项添加事件监听器
            document.getElementById('edit-messages').onclick = () => {
                menu.style.display = 'none';
                openMessagesEditor();
            };

            document.getElementById('edit-colors').onclick = () => {
                menu.style.display = 'none';
                openColorsEditor();
            };
        });

        // 点击其他地方隐藏菜单
        document.addEventListener('click', function() {
            document.getElementById('custom-context-menu').style.display = 'none';
        });
    }

    // 自定义右键菜单添加样式
    const style = document.createElement('style');
    style.textContent = `
    .custom-menu {
        position: absolute;
        display: none;
        background: white;
        border-radius: 8px;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        padding: 8px 0;
        z-index: 10000;
        min-width: 160px;
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    }

    .menu-item {
        padding: 8px 16px;
        cursor: pointer;
        font-size: 14px;
        color: #333;
        transition: background-color 0.2s;
    }

    .menu-item:hover {
        background-color: #f0f0f0;
    }

    .menu-divider {
        height: 1px;
        background-color: #eee;
        margin: 4px 0;
    }
`;
    document.head.appendChild(style);


    // 在页面加载完成后初始化
    document.addEventListener('DOMContentLoaded', function() {

        // 判断是否使用服务器模式，如果是则初始化自定义右键菜单和数据加载
        if (isServer === "server"){
            setupCustomContextMenu();
            loadMessagesAndColors();
        }

    });

    // --------------------------

    // 添加数据加载函数
    async function loadMessagesAndColors() {
        try {

            // 获取消息数据
            let messages = await getMessages();
            if (messages.length >= 0)
                defaultMessages = messages;


            // 获取颜色数据
            let colors = await getColors();
            if (colors.length >= 0)
                defaultColors = colors;


            console.log("123123123-------", defaultMessages, defaultColors)

        } catch (error) {
            console.error('数据加载失败:', error);
        }
    }

    // -------------------------------------

    // 便签信息管理
    function openMessagesEditor() {
        const editor = document.createElement('div');
        editor.id = 'messages-editor';
        editor.className = 'cute-editor';
        editor.innerHTML = `
        <div class="editor-overlay">
            <div class="editor-content">
                <div class="editor-header">
                    <h3>便签消息管理</h3>
                    <button class="close-btn" id="close-messages-editor">×</button>
                </div>
                <textarea id="messages-textarea" class="editor-textarea">${defaultMessages.join('\n')}</textarea>
                <div class="editor-footer">
                    <button class="save-btn" id="save-messages">保存到服务器</button>
                    <button class="cancel-btn" id="cancel-messages">取消</button>
                </div>
            </div>
        </div>
    `;
        document.body.appendChild(editor);

        // 添加样式
        addEditorStyles();

        // 绑定事件
        document.getElementById('close-messages-editor').onclick = () => editor.remove();
        document.getElementById('cancel-messages').onclick = () => editor.remove();
        document.getElementById('save-messages').onclick = saveMessagesToServer;
    }

    // 保存消息到服务器
    async function saveMessagesToServer() {
        try {
            const textArea = document.getElementById('messages-textarea');
            const newMessages = textArea.value.split('\n')
                .map(msg => msg.trim())
                .filter(msg => msg.length > 0);

            // 使用 api 文件中的方法
            await updateMessages(newMessages);

            alert('消息已成功保存到服务器！');
            // 更新本地 messages 数组
            defaultMessages.length = 0;
            defaultMessages.push(...newMessages);
            document.getElementById('messages-editor').remove();
        } catch (error) {
            console.error('保存失败:', error);
            alert('保存失败，请检查网络连接');
        }
    }

    // -------------------------------------

    // 便签颜色管理
    function openColorsEditor() {
        const editor = document.createElement('div');
        editor.id = 'colors-editor';
        editor.className = 'cute-editor';
        editor.innerHTML = `
        <div class="editor-overlay">
            <div class="editor-content">
                <div class="editor-header">
                    <h3>便签颜色管理</h3>
                    <button class="close-btn" id="close-colors-editor">×</button>
                </div>
                <textarea id="colors-textarea" class="editor-textarea">${defaultColors.map(color => `${color}`).join('\n')}</textarea>
                <div class="editor-footer">
                    <button class="save-btn" id="save-colors">保存到服务器</button>
                    <button class="cancel-btn" id="cancel-colors">取消</button>
                </div>
            </div>
        </div>
    `;
        document.body.appendChild(editor);

        // 添加样式
        addEditorStyles();

        // 绑定事件
        document.getElementById('close-colors-editor').onclick = () => editor.remove();
        document.getElementById('cancel-colors').onclick = () => editor.remove();
        document.getElementById('save-colors').onclick = saveColorsToServer;
    }

    // 保存颜色到服务器
    async function saveColorsToServer() {
        try {
            const textArea = document.getElementById('colors-textarea');
            const newColors = textArea.value.split('\n')
                .map(color => color.trim())
                .filter(color => color.length > 0 && color.startsWith('#'));

            // 使用 api 文件中的方法
            await updateColors(newColors);

            alert('颜色已成功保存到服务器！');
            // 更新本地 colors 数组
            defaultColors.length = 0;
            defaultColors.push(...newColors);
            document.getElementById('colors-editor').remove();
        } catch (error) {
            console.error('保存失败:', error);
            alert('保存失败，请检查网络连接');
        }
    }

    // -------------------------------------

    // 给两个管理页面添加样式
    function addEditorStyles() {
        if (document.getElementById('editor-styles')) return;

        const style = document.createElement('style');
        style.id = 'editor-styles';
        style.textContent = `
        .cute-editor {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 10001;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
        }

        .editor-overlay {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.3);
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .editor-content {
            background: white;
            border-radius: 12px;
            box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
            width: 90%;
            max-width: 500px;
            max-height: 80vh;
            display: flex;
            flex-direction: column;
        }

        .editor-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 16px;
            border-bottom: 1px solid #eee;
        }

        .editor-header h3 {
            margin: 0;
            color: #333;
            font-size: 18px;
        }

        .close-btn {
            background: none;
            border: none;
            font-size: 24px;
            cursor: pointer;
            color: #999;
            width: 30px;
            height: 30px;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 50%;
            transition: background-color 0.2s;
        }

        .close-btn:hover {
            background-color: #f0f0f0;
        }

        .editor-textarea {
            flex: 1;
            padding: 16px;
            border: none;
            resize: none;
            font-family: monospace;
            font-size: 14px;
            outline: none;
            min-height: 200px;
        }

        .editor-footer {
            display: flex;
            justify-content: flex-end;
            gap: 12px;
            padding: 16px;
            border-top: 1px solid #eee;
        }

        .save-btn, .cancel-btn {
            padding: 8px 16px;
            border-radius: 6px;
            border: none;
            cursor: pointer;
            font-size: 14px;
            transition: background-color 0.2s;
        }

        .save-btn {
            background: #4CAF50;
            color: white;
        }

        .save-btn:hover {
            background: #45a049;
        }

        .cancel-btn {
            background: #f1f1f1;
            color: #333;
        }

        .cancel-btn:hover {
            background: #e0e0e0;
        }
    `;
        document.head.appendChild(style);
    }








</script>
</body>
</html>